package com.zwcl.common.core.utils;

import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.zwcl.common.core.constant.Constants;
import com.zwcl.common.core.exception.UtilsException;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * TODO：这个工具类有缺陷，使用copyList时，针对dto中的LocalDateTime拷贝到Entity中的LocalDateTime报json序列化错误，待修复
 * @Description Bean相关工具类
 * @Author Jason
 * @Date 2019/9/4 10:54
 * @Email jason@wetax.com.cn
 */
public final class BeanUtilsEx extends BeanUtils {

    /**
     * Jackson转换对象
     */
    private static final ObjectMapper objectMapper = new ObjectMapper();

    /** Bean方法名中属性名开始的下标 */
    private static final int BEAN_METHOD_PROP_INDEX = 3;

    /** * 匹配getter方法的正则表达式 */
    private static final Pattern GET_PATTERN = Pattern.compile("get(\\p{javaUpperCase}\\w*)");

    /** * 匹配setter方法的正则表达式 */
    private static final Pattern SET_PATTERN = Pattern.compile("set(\\p{javaUpperCase}\\w*)");

    /**
     * 描述信息
     *
     * @param source 对象
     * @return the ms onion result
     * @Title deleteCacheByKeys
     * @Description 描述信息
     */
    public static String[] getNullPropertyNames(Object source) {
        final BeanWrapper src = new BeanWrapperImpl(source);
        PropertyDescriptor[] pds = src.getPropertyDescriptors();

        Set<String> emptyNames = new HashSet<String>();
        for (PropertyDescriptor pd : pds) {
            Object srcValue = src.getPropertyValue(pd.getName());
            if (srcValue == null) emptyNames.add(pd.getName());
        }
        String[] result = new String[emptyNames.size()];
        return emptyNames.toArray(result);
    }

    /**
     * 拷贝对象
     *
     * @param src        源对象
     * @param target     目标对象
     * @param IgnoreNull 是否NULL属性不拷贝
     */
    public static void copyPropertiesIgnoreNull(Object src, Object target, boolean IgnoreNull) {
        if (IgnoreNull) {
            BeanUtils.copyProperties(src, target, getNullPropertyNames(src));
        } else {
            copyProperties(src, target);
        }
    }

    /**
     * 拷贝不为null的属性
     *
     * @param source           source
     * @param target           target
     * @param ignoreProperties ignoreProperties
     * @throws UtilsException 异常
     */
    public static void copyPropertiesIgnoreNull(Object source, Object target, String... ignoreProperties) throws UtilsException {
        copyPropertiesNotNull(source, target, (Class) null, ignoreProperties);
    }


    /**
     * 重写 copyProperties
     *
     * @param source           source
     * @param target           target
     * @param editable         editable
     * @param ignoreProperties ignoreProperties
     * @throws UtilsException 异常
     */
    private static void copyPropertiesNotNull(Object source, Object target, Class<?> editable, String... ignoreProperties) throws UtilsException {
        Assert.notNull(source, "Source must not be null");
        Assert.notNull(target, "Target must not be null");
        Class<?> actualEditable = target.getClass();
        if (editable != null) {
            if (!editable.isInstance(target)) {
                throw new IllegalArgumentException("Target class [" + target.getClass().getName() + "] not assignable to Editable class [" + editable.getName() + "]");
            }

            actualEditable = editable;
        }

        PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
        List<String> ignoreList = ignoreProperties != null ? Arrays.asList(ignoreProperties) : null;
        PropertyDescriptor[] var7 = targetPds;
        int var8 = targetPds.length;

        for (int var9 = 0; var9 < var8; ++var9) {
            PropertyDescriptor targetPd = var7[var9];
            Method writeMethod = targetPd.getWriteMethod();
            if (writeMethod != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) {
                PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
                if (sourcePd != null) {
                    Method readMethod = sourcePd.getReadMethod();
                    if (readMethod != null && ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())) {
                        try {
                            if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
                                readMethod.setAccessible(true);
                            }

                            Object value = readMethod.invoke(source);
                            // 只拷贝不为null的属性
                            if (value != null) {
                                if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
                                    writeMethod.setAccessible(true);
                                }
                                writeMethod.invoke(target, value);
                            }
                        } catch (Throwable var15) {
                            throw new UtilsException("Could not copy property '" + targetPd.getName() + "' from source to target", var15);
                        }
                    }
                }
            }
        }

    }

    /**
     * 转为jaskson树
     *
     * @param bean 实体
     */
    public static JsonNode toJsonNode(Object bean) {
        //空值处理
        objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
        return objectMapper.valueToTree(bean);
    }

    /**
     * 转为map
     *
     * @param data 实体
     * @return map
     */
    public static Map bean2Map(Object data) {
        return objectMapper.convertValue(data, Map.class);
    }


    /**
     * List 拷贝
     *
     * @param sourceList 数据源集合
     * @param targetClz  目标对象class
     * @param <T>
     * @return 处理结果
     */
    public static <T> List copyList(List<T> sourceList, Class targetClz) {
        if (CollectionUtils.isEmpty(sourceList)) {
            return new ArrayList();
        }

        BeanUtils.copyProperties(sourceList.get(Constants.ZERO).getClass(), targetClz);
        return JsonUtils.jsonToList(JsonUtils.objectToJson(sourceList), targetClz);
    }

    /**
     * map 拷贝
     *
     * @param map 数据源
     * @return 处理结果
     */
    public static Map<String, Object> copyMap(Map map) {
        return JSON.parseObject(JSON.toJSONString(map));
    }

    /**
     * 获取对象的setter方法。
     *
     * @param obj 对象
     * @return 对象的setter方法列表
     */
    public static List<Method> getSetterMethods(Object obj)
    {
        // setter方法列表
        List<Method> setterMethods = new ArrayList<Method>();

        // 获取所有方法
        Method[] methods = obj.getClass().getMethods();

        // 查找setter方法

        for (Method method : methods)
        {
            Matcher m = SET_PATTERN.matcher(method.getName());
            if (m.matches() && (method.getParameterTypes().length == 1))
            {
                setterMethods.add(method);
            }
        }
        // 返回setter方法列表
        return setterMethods;
    }

    /**
     * 获取对象的getter方法。
     *
     * @param obj 对象
     * @return 对象的getter方法列表
     */

    public static List<Method> getGetterMethods(Object obj)
    {
        // getter方法列表
        List<Method> getterMethods = new ArrayList<Method>();
        // 获取所有方法
        Method[] methods = obj.getClass().getMethods();
        // 查找getter方法
        for (Method method : methods)
        {
            Matcher m = GET_PATTERN.matcher(method.getName());
            if (m.matches() && (method.getParameterTypes().length == 0))
            {
                getterMethods.add(method);
            }
        }
        // 返回getter方法列表
        return getterMethods;
    }

    /**
     * 检查Bean方法名中的属性名是否相等。<br>
     * 如getName()和setName()属性名一样，getName()和setAge()属性名不一样。
     *
     * @param m1 方法名1
     * @param m2 方法名2
     * @return 属性名一样返回true，否则返回false
     */

    public static boolean isMethodPropEquals(String m1, String m2)
    {
        return m1.substring(BEAN_METHOD_PROP_INDEX).equals(m2.substring(BEAN_METHOD_PROP_INDEX));
    }
}
